/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.java; import java.io.*; import java.lang.ref.*; import java.util.*; import javax.swing.text.BadLocationException; import javax.swing.text.StyledDocument; import javax.swing.text.Position; import org.openide.filesystems.FileObject; import org.openide.loaders.MultiDataObject; import org.openide.loaders.DataObject; import org.openide.util.*; import org.openide.nodes.Node; import org.openide.nodes.Children; import org.openide.text.PositionRef; import org.openide.text.PositionBounds; import org.openide.text.NbDocument; import org.openide.text.EditorSupport; import org.openide.src.*; import org.netbeans.modules.java.gj.ParserEngine; /** Implementation of the Source element for java source files. * This object mainly takes care about the parsing management. * * @author Petr Hamernik */ class SourceElementImpl extends ElementImpl implements SourceElement.Impl { // ======================= Static Part ================================== static final long serialVersionUID = -5791016681797582759L; /** Request processor used for parsing. */ static final RequestProcessor PARSING_RP = new RequestProcessor("Java Source Parsing"); /** Reference to <CODE>null</CODE> */ static final Reference EMPTY_REF = new WeakReference(null); // ======================= Instance Part ================================== /** Appropriate java data object. */ JavaDataObject jdo; /** Current status of the parsing. One of the STATUS_XXX constants. */ int status = SourceElement.STATUS_NOT; /** Reference to the data and parsing runnable. */ Reference dataRef = EMPTY_REF; /** If the parsing is in the progress this variable is set to the parsing task. */ RequestProcessor.Task parsingTask = null; /** Current serial number of last dataRef object created for this object. */ int dataRefCounter = 0; /** This flag is set when somebody edit the document and it is * cleared after reparsing. * It is used by parser to decide if there is required to collect * parsing information. */ boolean dirty = false; /** This reference holds the dataref when source contains no classes. * Otherwise it is null. */ DataRef hookForEmptySources = null; private static final boolean DEBUG = false; // static HashMap m = new HashMap(); private static Reference v8Engine = EMPTY_REF; static ParserEngine getCurrentV8Engine() { ParserEngine e = (ParserEngine) v8Engine.get(); if (e == null) { e = ParserEngine.makeParsingEngine(); if (Boolean.getBoolean("netbeans.debug.heap")) { v8Engine = new WeakReference(e); } else { v8Engine = new SoftReference(e); } } return e; } protected Object getBodyHash() { return null; } // ======================== Public part ==================================== /** Constructs the implementation of source element for the given * java data object. */ public SourceElementImpl(JavaDataObject jdo) { super(null); this.jdo = jdo; } /** Getter for the current status of the SourceElement implementation. * @return the status - one of the STATUS_XXX constants. */ public int getStatus() { int ret = status; return ret; } /** Method that instructs the implementation of the source element * to prepare the element. It is non blocking method that returns * task that can be used to control if the operation finished or not. * * @return task to control the preparation of the elemement */ public Task prepare() { return parseObject(Thread.MAX_PRIORITY - 1, true); } /** Sets package to the id. The id can be <CODE>null</CODE> if * the package should be set to default package (cleared) * @param id the identifier * @exception SourceException if the operation cannot proceed */ public void setPackage (Identifier id) throws SourceException { final DataRef d = getData(); try { String newValue = (id == null) ? "" : "package "+id.getFullName() + ";"; PositionBounds bounds; final EditorSupport supp = (EditorSupport)jdo.getCookie(JavaEditor.class); if (d.packageId != null) { if (id != null && id.compareTo(d.packageId, true)) { return; } bounds = d.packageBounds; } else { if (id == null) { return; } bounds = createPackageBounds(supp); newValue = newValue + "\n"; } final StyledDocument doc = supp.openDocument(); final PositionBounds[] holder = new PositionBounds[] { bounds }; final String text = newValue; Util.runAtomic(doc, new Util.ExceptionRunnable() { public void run() throws Exception { StringWriter stringWriter = new StringWriter(); PositionBounds b = d.packageId == null ? createNewLineBoundsAt(holder[0].getBegin()) : holder[0]; Writer indentWriter = Util.findIndentWriter(doc, b.getBegin().getOffset(), stringWriter); indentWriter.write(text.toString()); indentWriter.flush(); b.setText(stringWriter.toString()); holder[0] = b; } }); org.openide.src.Identifier old = d.packageId; d.packageId = id; d.packageBounds = holder[0]; firePropertyChange (PROP_PACKAGE, old, d.packageId); } catch (IOException e) { throw new SourceException(e.getMessage()); } } private PositionBounds createPackageBounds(EditorSupport editor) throws SourceException { DataRef d = (DataRef)getData(); if (d.imports != null && d.imports.length > 0) { return d.importsBounds[0]; } if (d.classes != null && d.classes.size() > 0) { return ((ElementImpl)d.classes.getFirst().getCookie(ElementImpl.class)).bounds; } return new PositionBounds(editor.createPositionRef(0, Position.Bias.Forward), editor.createPositionRef(0, Position.Bias.Backward)); } /** @return the package id or <CODE>null</CODE> if we are in default package */ public org.openide.src.Identifier getPackage () { try { DataRef d = getData(); return d.packageId; } catch (SourceException e) { e.printStackTrace(); return null; } } /** @return the imports */ public Import[] getImports() { try { DataRef d = getData(); return d.imports; } catch (SourceException e) { e.printStackTrace(); return null; } } /** Changes set of elements. * @param elems elements to change * @param action the action to do (ADD, REMOVE, SET) * @exception SourceException if the action cannot be handled */ public void changeImports (Import[] elems, int action) throws SourceException { boolean changed = false; DataRef d = getData(); Import[] oldImports = d.imports; try { switch (action) { case SET: { changed = true; for (int i = 0; i < d.imports.length; i++) { clearBounds(d.importsBounds[i]); } d.imports = new Import[0]; d.importsBounds = new PositionBounds[0]; // FALL THROUGH } case ADD: { int i; List newElems = new LinkedList(); for (i = 0; i < elems.length; i++) { Import im = elems[i]; if (!hasImport(im)) { newElems.add(im); } } if (newElems.size() > 0) { Import[] newImports = new Import[d.imports.length + newElems.size()]; PositionBounds[] newBounds = new PositionBounds[newImports.length]; try { if (d.imports != null) { for (i = 0; i < d.imports.length; i++) { newImports[i] = d.imports[i]; newBounds[i] = d.importsBounds[i]; } } final EditorSupport supp = (EditorSupport)jdo.getCookie(JavaEditor.class); final StyledDocument doc = supp.openDocument(); Iterator it = newElems.iterator(); final PositionBounds[] holder = new PositionBounds[] { createNewImportBounds(supp) }; while (it.hasNext()) { final Import im = (Import)it.next(); Util.runAtomic(doc, new Util.ExceptionRunnable() { public void run() throws Exception { StringWriter stringWriter = new StringWriter(); PositionBounds b = createNewLineBoundsAt(holder[0].getEnd()); Writer indentWriter = Util.findIndentWriter(doc, b.getBegin().getOffset(), stringWriter); indentWriter.write(im.toString() + ";"); indentWriter.flush(); b.setText(stringWriter.toString()); holder[0] = b; } }); newImports[i] = im; newBounds[i++] = holder[0]; changed = true; } d.imports = newImports; d.importsBounds = newBounds; } finally { } } break; } case REMOVE: { boolean[] removes = new boolean[d.imports == null ? 1 : d.imports.length]; int removeCount = 0; try { for (int i = 0; i < elems.length; i++) { Import im = elems[i]; for (int j = 0; j < d.imports.length; j++) { if (d.imports[j].equals(im)) { clearBounds(d.importsBounds[j]); removes[j] = true; removeCount++; changed = true; break; } } } } finally { if (removeCount > 0) { Import[] newImps = new Import[d.imports.length - removeCount]; PositionBounds[] newBounds = new PositionBounds[newImps.length]; int index = 0; for (int i = 0; i < d.imports.length; i++) { if (!removes[i]) { newImps[index] = d.imports[i]; newBounds[index++] = d.importsBounds[i]; } } } } } } } catch (SourceException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new SourceException(e.getMessage()); } finally { if (changed) { firePropertyChange(PROP_IMPORTS, oldImports, d.imports); } } } /** Finds out if a particular import is made in the source file. @param im Import object that is to be found. @throws SourceException if the source file can not be parsed. */ public boolean hasImport(Import im) throws SourceException { Import[] ims = getData().imports; if (ims == null) return false; for (int i = 0; i < ims.length; i++) { if (ims[i].equals(im)) { return true; } } return false; } /** Changes set of elements. * @param elems elements to change * @exception SourceException if the action cannot be handled */ public void changeClasses (ClassElement[] elems, int action) throws SourceException { DataRef d = getData(); d.classes.change(elems, action); hookForEmptySources = (d.classes.size() == 0) ? d : null; } /** * @return the classes of this source. */ public ClassElement[] getClasses() { try { DataRef d = getData(); return (ClassElement[]) d.classes.toArray(); } catch (SourceException e) { e.printStackTrace(); return new ClassElement[] {}; } } /** Finds an inner class with given name. * @param name the name to look for * @return the element or null if such class does not exist */ public ClassElement getClass (org.openide.src.Identifier name) { try { DataRef d = getData(); return (ClassElement)d.classes.find(name, null); } catch (SourceException e) { e.printStackTrace(); return null; } } /** Get for all classes (top level and inner classes) */ public ClassElement[] getAllClasses () { LinkedList list = new LinkedList(); ClassElement[] classes = getClasses(); for (int i = 0; i < classes.length; i++) addAllClasses(classes[i], list); ClassElement[] allClasses = new ClassElement[list.size()]; list.toArray(allClasses); return allClasses; } /** Read resolve. */ public Object readResolve() { return new SourceElement(this); } /** Lock the underlaing document to have exclusive access to it and could make changes * on this SourceElement. * * @param run the action to run */ public void runAtomic (Runnable run) { JavaEditor ed = (JavaEditor) jdo.getCookie(JavaEditor.class); Task t = ed.prepareDocument(); t.waitFinished(); StyledDocument doc = ed.getDocument(); if (doc == null) run.run(); else NbDocument.runAtomic(doc, run); } /** Executes given runnable in "user mode" does not allowing any modifications * to parts of text marked as guarded. The actions should be run as "atomic" so * either happen all at once or none at all (if a guarded block should be modified). * * @param run the action to run * @exception SourceException if a modification of guarded text occured * and that is why no changes to the document has been done. */ public void runAtomicAsUser (Runnable run) throws SourceException { JavaEditor ed = (JavaEditor) jdo.getCookie(JavaEditor.class); Task t = ed.prepareDocument(); t.waitFinished(); StyledDocument doc = ed.getDocument(); if (doc == null) run.run(); else { try { NbDocument.runAtomicAsUser(doc, run); } catch (BadLocationException e) { throw new SourceException(e.getMessage()); } } } /** Find the element at the specified offset in the document. * @param offset The position of the element * @return the element at the position. */ public Element findElement(int offset) { try { DataRef d = getData(); Element retElement = d.classes.findElement(offset); if (retElement != null) return retElement; } catch (SourceException e) { } return element; } public void markCurrent(boolean beforeAfter) { } /** Get a cookie from the node. * * @param type the representation class * @return the cookie or <code>null</code> */ public Node.Cookie getCookie (Class type) { Node.Cookie c = super.getCookie(type); if (c == null) { if (type.equals(DataObject.class) || type.equals(MultiDataObject.class) || JavaDataObject.class.isAssignableFrom(type)) { return jdo; } } return c; } // ======================== Package private part ================================ JavaDataObject getJavaDataObject() { return jdo; } /** Sets the dirty flag - if the document was modified after last parsing. */ void setDirty(boolean b) { dirty = b; } /** Tests the dirty flag. */ boolean isDirty() { return dirty; } /** Starts the parsing if the this class is 'dirty' and status != STATUS_NOT * and parsing is not running yet. @return parsing task so caller may listen on its completion. */ Task autoParse() { return parseObject(Thread.MIN_PRIORITY, false); } /** Starts the parsing if the this class is 'dirty' and status != STATUS_NOT * and parsing is not running yet. @return parsing task so caller may listen on its completion. */ Task afterSaveParse() { return parseObject(Thread.MIN_PRIORITY, true); } SourceElementImpl findSourceElementImpl() { return this; } void registerForName(Identifier id, JavaConnections.Type type) { jdo.registerForName(id, type); } void unregisterForName(Identifier id, JavaConnections.Type type) { jdo.unregisterForName(id, type); } // ======================== Private part ================================ private synchronized Task parseObject(int priority, boolean forceParsing) { RequestProcessor.Task t = parsingTask; DataRef d = (DataRef) dataRef.get(); if (t == null) { if (d == null) { d = new DataRef(jdo, ++dataRefCounter); } d.forceParsing = forceParsing; t = PARSING_RP.post(d, 0, priority); parsingTask = t; } else { if (t.cancel()) { t.schedule(0); } t.setPriority(priority); //PENDING - jenom zvysovat //PENDING - set forceParsing } return t; } /** * * @return the DataRef object holding the parsing information * @exception SourceException if parsing failed. */ private DataRef getData() throws SourceException { DataRef d = (DataRef) dataRef.get(); if (d != null) return d; Task t = prepare(); t.waitFinished(); d = (DataRef) dataRef.get(); if (d != null) return d; //printLog(jdo); // IMPORTANT NOTICE: // Don't remove this line. It is necessary to hold 't' local variable here, because otherwise // sometimes happen that HotSpot optimize this method and garbage collector can release // the object holded by dataRef Reference object. [Petr Hamernik] t.isFinished(); // --------------------------- //System.out.println ("BLBE:"+jdo.getName()); throw new SourceException(Util.getString("EXC_CannotParse")); } /** Adds the class to the list and also all its innerclasses. * @param c ClassElement to add * @param list where to add. */ private void addAllClasses(ClassElement c, LinkedList list) { list.add(c); ClassElement[] innerClasses = c.getClasses(); for (int i = 0; i < innerClasses.length; i++) { addAllClasses(innerClasses[i], list); } } /** Informs the SourceElement about releasing data (classes, imports,...) * from the memory. This method gets as the parameter DataRef which will be * garbage collected and should swap them to the disk. */ private void dataRefReleased(final DataRef releasedData) { PARSING_RP.post(new Runnable() { public void run() { if (releasedData.number == dataRefCounter) { int old = status; status = SourceElement.STATUS_NOT; // String n = jdo.getPrimaryFile().getPackageName('.'); m.remove(n); firePropertyChange(PROP_STATUS, new Integer(old), new Integer(SourceElement.STATUS_NOT)); } } }, Thread.MAX_PRIORITY); } /** Fire change of cookies to all elements, if this object is parsed. * Used for propagating cookies changes from JavaDataObject to ElementNodes. */ void fireCookiesChange() { DataRef d = (DataRef) dataRef.get(); if (d != null) { ClassElement[] classes = getAllClasses(); for (int i = 0; i < classes.length; i++) { Element[] els; for (int j = 0; j <= 3; j++) { switch (j) { case 0: els = classes[i].getInitializers(); break; case 1: els = classes[i].getFields(); break; case 2: els = classes[i].getConstructors(); break; default: els = classes[i].getMethods(); break; } for (int k = 0; k < els.length; k++) { ElementImpl impl = (ElementImpl) els[k].getCookie(ElementImpl.class); impl.firePropertyChange(Node.PROP_COOKIE, null, null); } } ElementImpl impl = (ElementImpl) classes[i].getCookie(ElementImpl.class); impl.firePropertyChange(Node.PROP_COOKIE, null, null); } } } PositionBounds createNewImportBounds(EditorSupport editor) throws SourceException { DataRef d = getData(); if (d.imports != null && d.imports.length > 0) { return d.importsBounds[d.imports.length - 1]; } int offset = 0; if (d.packageId != null) { offset = d.packageBounds.getEnd().getOffset() + 1; } return new PositionBounds(editor.createPositionRef(offset, Position.Bias.Forward), editor.createPositionRef(offset, Position.Bias.Backward)); } /** Creates bounds for specific element type (currently only class elements) */ PositionBounds createBoundsFor(ElementsCollection col) throws SourceException { DataRef d = getData(); if (col != d.classes) { throw new InternalError("Invalid parent for collection " + col.toString()); } ClassElement celem = (ClassElement)d.classes.getLast(); if (celem != null) { ClassElementImpl clazz = (ClassElementImpl)celem.getCookie(ElementImpl.class); return createNewLineBoundsAt(clazz.bounds.getEnd()); } try { JavaEditor jed = jdo.getJavaEditor(); javax.swing.text.StyledDocument doc = jed.openDocument(); Position pos = doc.getEndPosition(); PositionRef posref = jdo.getJavaEditor().createPositionRef(pos.getOffset(), Position.Bias.Backward); return createNewLineBoundsAt(posref); } catch (Exception e) { if (Boolean.getBoolean("netbeans.debug.exceptios")) // NOI18N e.printStackTrace(); throw new SourceException(e.getLocalizedMessage()); } } PositionRef findUnguarded(PositionRef pos, PositionBounds limits) throws SourceException { PositionRef result; result = jdo.getJavaEditor().findUnguarded(pos, false, true); if (limits.getBegin().getOffset() > result.getOffset() || limits.getEnd().getOffset() < result.getOffset()) { } return result; } /** Creates bounds for a new line after the specified position. If the line containing `where' is not empty, insertion point is placed right after the line. */ static PositionBounds createNewLineBoundsAt(PositionRef where) throws SourceException { try { EditorSupport editor = where.getEditorSupport(); javax.swing.text.StyledDocument doc = editor.openDocument(); int beginText = where.getOffset(); int lineIndex = NbDocument.findLineNumber(doc, beginText); javax.swing.text.Element line = NbDocument.findLineRootElement(doc).getElement(lineIndex); int lineBegin = line.getStartOffset(); int lineEnd = line.getEndOffset(); String lineStr = doc.getText(lineBegin, beginText - lineBegin); String trimLine = lineStr.trim(); int newBlockOffset = lineBegin; if (trimLine.length() > 0) { // move to the next line ;-) newBlockOffset += lineStr.lastIndexOf(trimLine) + trimLine.length(); } PositionRef posBegin = editor.createPositionRef(newBlockOffset, Position.Bias.Forward); PositionRef posEnd = editor.createPositionRef(newBlockOffset, Position.Bias.Backward); PositionBounds bounds = new PositionBounds(posBegin, posEnd); if (newBlockOffset == lineEnd || newBlockOffset == lineBegin) { bounds.insertAfter("\n"); // NOI18N } else { bounds.insertAfter("\n\n"); // NOI18N } if (newBlockOffset == lineBegin) { return bounds; } posBegin = editor.createPositionRef(newBlockOffset + 1, Position.Bias.Forward); posEnd = editor.createPositionRef(newBlockOffset + 1, Position.Bias.Backward); return new PositionBounds(posBegin, posEnd); } catch (Exception e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N e.printStackTrace(); throw new SourceException(e.getMessage()); } } /** Creates new bounds at the given position. If the line containing `where' is not empty, the function skips to the end of the line. */ static PositionBounds createBoundsAt(PositionRef where) throws SourceException { try { EditorSupport editor = where.getEditorSupport(); javax.swing.text.StyledDocument doc = editor.openDocument(); int beginText = where.getOffset(); int lineIndex = NbDocument.findLineNumber(doc, beginText); javax.swing.text.Element line = NbDocument.findLineRootElement(doc).getElement(lineIndex); int lineBegin = line.getStartOffset(); int lineEnd = line.getEndOffset(); String lineStr = doc.getText(lineBegin, beginText - lineBegin); String trimLine = lineStr.trim(); int newBlockOffset = lineBegin; if (trimLine.length() > 0) newBlockOffset += lineStr.lastIndexOf(trimLine) + trimLine.length(); PositionRef posBegin = editor.createPositionRef(newBlockOffset, Position.Bias.Forward); PositionRef posEnd = editor.createPositionRef(newBlockOffset, Position.Bias.Backward); PositionBounds bounds = new PositionBounds(posBegin, posEnd); bounds.insertAfter("\n"); // NOI18N return bounds; } catch (Exception e) { if (Boolean.getBoolean("netbeans.debug.exceptios")) // NOI18N e.printStackTrace(); throw new SourceException(e.getMessage()); } } static void clearBounds(final PositionBounds bounds) throws SourceException { final Exception[] hold = new Exception[] { null }; try { final javax.swing.text.StyledDocument doc = bounds.getBegin().getEditorSupport().openDocument(); Runnable run = new Runnable() { public void run() { // remove the range of the element. int p1 = bounds.getBegin().getOffset(); int p2 = bounds.getEnd().getOffset(); int p3 = 0; try { doc.remove(p1, p2 - p1); } catch (BadLocationException e) { hold[0] = e; return; } // remove empty space where was the element placed int lineIndex = NbDocument.findLineNumber(doc, p1); javax.swing.text.Element lineRoot; lineRoot = NbDocument.findLineRootElement(doc); int lineCount = lineRoot.getElementCount(); javax.swing.text.Element line = lineRoot.getElement(lineIndex); p1 = line.getStartOffset(); p2 = line.getEndOffset(); if (lineIndex < lineCount - 1) { line = NbDocument.findLineRootElement(doc).getElement(lineIndex + 1); p3 = line.getStartOffset(); } else { p3 = p2; } try { String lineStr = doc.getText(p1, p2 - p1).trim(); if (lineStr.length() == 0) { doc.remove(p1, p3 - p1); } } catch (BadLocationException e) { } } }; NbDocument.runAtomic (doc, run); } catch (IOException e) { hold[0] = e; } if (hold[0] != null) throw new SourceException(hold[0].getMessage()); } // ======================== Realy data holder ========================== /** Class which is used for holding the parsed information. * The source element impl holds only Reference to this object. */ private static class DataRef extends Object implements Runnable { /** A serial version UID */ static final long serialVersionUID = 697350931687937673L; /** Appropriate java data object. */ JavaDataObject jdo; /** The sequence number of the this object created for one SourceElement. */ int number; /** This really means `force results to be displayed', not force parsing to happen ;-) */ boolean forceParsing = true; boolean virgin = true; // --------------- Data ------------------- /** Package for the source */ org.openide.src.Identifier packageId; /** The bound of the package declaration. Could be null * for default package. */ PositionBounds packageBounds; /** Array of the imports. */ Import[] imports; /** Position bounds for each import. */ PositionBounds[] importsBounds; /** Collection of the classes */ ElementsCollection.Class classes; /** Creates new data holder. */ DataRef(JavaDataObject jdo, int number) { this.jdo = jdo; this.number = number; packageId = null; packageBounds = null; imports = new Import[] {}; importsBounds = new PositionBounds[] {}; classes = new ElementsCollection.Class(jdo.getSourceElementImpl()); } /** Informs the SourceElementImpl about the releasing * of this class from the memory. */ public void finalize() throws Throwable { jdo.getSourceElementImpl().dataRefReleased(this); super.finalize(); } public void run() { try { doIt(); // without this, the null value might be overwritten by another thread, that is currently // inside parseObject (in case that the parsing has finished *very* quickly) } catch (Exception e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { e.printStackTrace(); } } finally { synchronized(jdo.getSourceElementImpl()) { jdo.getSourceElementImpl().parsingTask = null; } } } /** Run the task. */ public void doIt() { final SourceElementImpl impl = jdo.getSourceElementImpl(); if (!virgin) { if ((!forceParsing && (!impl.dirty || (impl.status == SourceElement.STATUS_NOT))) || (forceParsing && !impl.dirty && (impl.status != SourceElement.STATUS_NOT))) { return; } } // parse boolean partialError; V8ParseRequest request; ParsingResult result; ParserEngine engine = getCurrentV8Engine(); synchronized(engine.getSyncObject()) { request = new V8ParseRequest(jdo); do { impl.setDirty(false); engine.parseObject(request); result = request.result; partialError = request.getSyntaxErrors() > 0; if (!virgin && !forceParsing && (partialError || (result == null))) { // if parsing was forced (== results have to be updated), update even if there are some errors. return; } } while (impl.isDirty()); } // clear internal compiler tables after parsing... //engine.clear(); virgin = false; forceParsing = false; int oldStatus = impl.status; final LinkedList changes = new LinkedList(); int changesMask = jdo.getAllListenersMask(); int newStatus = oldStatus; if (result == null) { result = new ParsingResult(); newStatus = SourceElement.STATUS_ERROR; } else { newStatus = partialError ? SourceElement.STATUS_PARTIAL : SourceElement.STATUS_OK; } boolean checkInterfaces = ((impl.status == SourceElement.STATUS_OK) && (impl.status != oldStatus)); // set package packageBounds = result.packageBounds; boolean changed = (packageId == null) && (result.packageId != null); changed |= (packageId != null) && (result.packageId == null); if (changed || ((packageId != null) && !packageId.equals(result.packageId))) { Identifier oldId = packageId; packageId = result.packageId; if (oldStatus == SourceElement.STATUS_OK) impl.firePropertyChange (PROP_PACKAGE, oldId, packageId); } // imports changed = (imports.length != result.imports.size()); Import[] newImports = new Import[result.imports.size()]; result.imports.toArray(newImports); if (!changed) { int size = imports.length; for (int i = 0; i < size; i++) { if (!imports[i].equals(newImports[i])) { changed = true; break; } } } importsBounds = new PositionBounds[result.importsBounds.size()]; result.importsBounds.toArray(importsBounds); if (changed) { Import[] oldImports = imports; imports = newImports; impl.firePropertyChange(PROP_IMPORTS, oldImports, imports); } // classes classes.updateContent(result.classes, changes, changesMask); ClassElement[] classArray = (ClassElement[]) classes.toArray(); for (int i = 0; i < classArray.length; i++) { ((ClassElementImpl)classArray[i].getCookie(ClassElementImpl.class)).hook = this; } impl.dataRef = new WeakReference(this); impl.status = newStatus; impl.hookForEmptySources = (classes.size() == 0) ? this : null; // fires the change of the status - it is required to fire it everytime // because of the icon changes in the delegate node. impl.firePropertyChange (PROP_STATUS, null, null); Parsing.fireEvent(jdo, impl.hookForEmptySources); // Connections if (changes.size() > 0) { PARSING_RP.post(new Runnable() { public void run() { fireJavaConnections(changes); } }, Thread.MIN_PRIORITY); } if (packageId != null) { String tmp = packageId.getFullName(); if (!tmp.startsWith("java.") && !tmp.startsWith("javax.")) { final Object hookForTheFollowingRunnable = this; if (checkInterfaces && JavaConnections.SETTINGS.isEnabled()) { PARSING_RP.post(new Runnable() { public void run() { if (hookForTheFollowingRunnable != null) { //only gc prevention (may be some optim.) ClassElement[] classes2 = impl.getAllClasses(); for (int i = 0; i < classes2.length; i++) { ((ClassElementImpl)classes2[i].getCookie(ClassElementImpl.class)).checkInterfaces(); } } } }, Thread.MIN_PRIORITY); } } } // Store the hook to the editor if it has loaded document. JavaEditor ed = jdo.getJavaEditor(); if (ed.isDocumentLoaded()) { ed.parsedHook = this; } } void fireJavaConnections(LinkedList changes) { JavaConnections.Change[] arr = new JavaConnections.Change[changes.size()]; changes.toArray(arr); int wholeEventMask = 0; for (int i = 0; i < arr.length; i++) { wholeEventMask |= arr[i].getChangeType(); } JavaConnections.Type type = new JavaConnections.Type(wholeEventMask); JavaConnections.Event event = new JavaConnections.Event(jdo.getNodeDelegate(), arr, type); jdo.getConnectionSupport().fireEvent(event); } } // ======================== Utility - Debug ========================== static HashMap map = new HashMap(); static void addLog(JavaDataObject jdo, String msg) { /* String n = jdo.getPrimaryFile().getPackageName('.'); LinkedList list = (LinkedList) map.get(n); if (list == null) { list = new LinkedList(); map.put(n, list); } String t = Thread.currentThread().getName(); list.add(t+": "+msg); */ } static void printLog(JavaDataObject jdo) { /* String n = jdo.getPrimaryFile().getPackageName('.'); System.out.println ("========================= "+n+" =============================="); LinkedList list = (LinkedList) map.get(n); if (list != null) { java.util.Iterator it = list.iterator(); while (it.hasNext()) { System.out.println (it.next()); } } */ } } /* * Log * 79 Gandalf-post-FCS1.65.2.12 4/18/00 Svatopluk Dedic afterSaveParse dirties * the data so they're parsed in any case. * 78 Gandalf-post-FCS1.65.2.11 4/17/00 Svatopluk Dedic Fixed new line insertion * code * 77 Gandalf-post-FCS1.65.2.10 4/14/00 Svatopluk Dedic Adjusted dirty flag * clearing. * 76 Gandalf-post-FCS1.65.2.9 4/5/00 Svatopluk Dedic * 75 Gandalf-post-FCS1.65.2.8 4/4/00 Svatopluk Dedic Corrected * error/up-to-date parsing data checking * 74 Gandalf-post-FCS1.65.2.7 4/3/00 Svatopluk Dedic Sync improved * 73 Gandalf-post-FCS1.65.2.6 4/3/00 Svatopluk Dedic Improved package * setting; fixed unnecessary parsing task scheduling. * 72 Gandalf-post-FCS1.65.2.5 3/27/00 Svatopluk Dedic Disabled computation of * body hash; no PROP_BODY change event is ever fired. * 71 Gandalf-post-FCS1.65.2.4 3/13/00 Svatopluk Dedic Rest of addlog debugging * removed * 70 Gandalf-post-FCS1.65.2.3 3/8/00 Svatopluk Dedic Better error reporting; * addLogs removed * 69 Gandalf-post-FCS1.65.2.2 3/6/00 Svatopluk Dedic Changed * construction/synchronization of parsing engine * 68 Gandalf-post-FCS1.65.2.1 2/24/00 Svatopluk Dedic After autoparse, a node * corresponding to editor caret's position will be selected * 67 Gandalf-post-FCS1.65.2.0 2/24/00 Ian Formanek Post FCS changes * 66 src-jtulach1.65 2/15/00 Svatopluk Dedic Debug output commented * out * 65 src-jtulach1.64 2/14/00 Svatopluk Dedic Fixed #5283 * 64 src-jtulach1.63 1/18/00 Petr Hamernik message removed * 63 src-jtulach1.62 1/18/00 Petr Hamernik fixed #2119 * 62 src-jtulach1.61 1/13/00 Petr Hamernik i18n -(2nd round) - * script bug fixed. * 61 src-jtulach1.60 1/12/00 Petr Hamernik i18n: perl script used ( * //NOI18N comments added ) * 60 src-jtulach1.59 11/27/99 Patrik Knakal * 59 src-jtulach1.58 10/27/99 Petr Hamernik synchronization is * disabled for java.* and javax.* classes * 58 src-jtulach1.57 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems copyright in file comment * 57 src-jtulach1.56 10/7/99 Petr Hamernik fixed bugs #2946, #3621 * - cookies changes of JavaDO (e.g.save) is propagated to all * ElementNodes * 56 src-jtulach1.55 10/7/99 Petr Hamernik Java module has its own * RequestProcessor for source parsing. * 55 src-jtulach1.54 9/27/99 Petr Hamernik debug printing removed * 54 src-jtulach1.53 9/24/99 Petr Hamernik closing stream and other * improvements * 53 src-jtulach1.52 9/13/99 Petr Hamernik fixed bug #2078 - firing * of IMPORT property. * 52 src-jtulach1.51 9/13/99 Petr Hamernik runAtomicAsUser * implementation * 51 src-jtulach1.50 9/10/99 Petr Hamernik threading model changes, * props of synchronization removed * 50 src-jtulach1.49 8/18/99 Petr Hamernik i18n * 49 src-jtulach1.48 8/17/99 Petr Hamernik parsing is invoked sync * in AWT-Event-Queue (otherwise is sent to RequestProcessor) * 48 src-jtulach1.47 8/6/99 Petr Hamernik Working with threads * improved * 47 src-jtulach1.46 7/23/99 Petr Hamernik global parsing listener * 46 src-jtulach1.45 7/19/99 Petr Hamernik findElement(int) * implemented * 45 src-jtulach1.44 7/17/99 Petr Hamernik deadlock prevention * 44 src-jtulach1.43 7/8/99 Petr Hamernik changes reflecting * org.openide.src changes * 43 src-jtulach1.42 7/3/99 Petr Hamernik SourceCookie.Editor - * 1st version * 42 src-jtulach1.41 6/9/99 Ian Formanek ---- Package Change To * org.openide ---- * 41 src-jtulach1.40 6/7/99 Petr Hamernik small improvement * 40 src-jtulach1.39 6/5/99 Petr Hamernik synchronization problem * fixed * 39 src-jtulach1.38 6/4/99 Petr Hamernik synchronization update * 38 src-jtulach1.37 6/2/99 Petr Hamernik connections of java * sources * 37 src-jtulach1.36 5/21/99 Petr Hamernik parsing info garbage * collected too fast - fixed * 36 src-jtulach1.35 5/15/99 Petr Hamernik fixed bug #1752 * 35 src-jtulach1.34 5/14/99 Petr Hamernik bufix in parsing after * close * 34 src-jtulach1.33 5/13/99 Petr Hamernik changes in comparing * Identifier, Type classes * 33 src-jtulach1.32 5/12/99 Petr Hamernik ide.src.Identifier * changed * 32 src-jtulach1.31 5/10/99 Petr Hamernik * 31 src-jtulach1.30 4/28/99 Petr Hamernik simple synchronization * using ConnectionCookie * 30 src-jtulach1.29 4/23/99 Petr Hamernik Mutex synchr improved * 29 src-jtulach1.28 4/23/99 Petr Hamernik MUTEX synchronization * changed * 28 src-jtulach1.27 4/21/99 Petr Hamernik Java module updated * 27 src-jtulach1.26 4/21/99 Petr Hamernik performance improved * 26 src-jtulach1.25 4/16/99 Petr Hamernik synchronization under * Nodes.MUTEX * 25 src-jtulach1.24 4/15/99 Petr Hamernik parser improvements * 24 src-jtulach1.23 4/7/99 Petr Hamernik synchronization improved * 23 src-jtulach1.22 4/6/99 Petr Hamernik bugfixes * 22 src-jtulach1.21 4/4/99 Ian Formanek Patched to prevent * errors during compilation of Form DataObjects * 21 src-jtulach1.20 4/1/99 Petr Hamernik * 20 src-jtulach1.19 4/1/99 Petr Hamernik * 19 src-jtulach1.18 3/29/99 Petr Hamernik * 18 src-jtulach1.17 3/29/99 Petr Hamernik * 17 src-jtulach1.16 3/29/99 Petr Hamernik * 16 src-jtulach1.15 3/19/99 Petr Hamernik simple temp hack * 15 src-jtulach1.14 3/18/99 Petr Hamernik * 14 src-jtulach1.13 3/12/99 Petr Hamernik * 13 src-jtulach1.12 3/10/99 Petr Hamernik * 12 src-jtulach1.11 3/2/99 Jan Jancura * 11 src-jtulach1.10 2/25/99 Petr Hamernik * 10 src-jtulach1.9 2/19/99 Petr Hamernik * 9 src-jtulach1.8 2/18/99 Petr Hamernik * 8 src-jtulach1.7 2/18/99 Petr Hamernik * 7 src-jtulach1.6 2/17/99 Petr Hamernik * 6 src-jtulach1.5 2/17/99 Petr Hamernik * 5 src-jtulach1.4 2/17/99 Petr Hamernik * 4 src-jtulach1.3 2/12/99 Petr Hamernik * 3 src-jtulach1.2 2/12/99 Petr Hamernik * 2 src-jtulach1.1 2/11/99 Petr Hamernik * 1 src-jtulach1.0 2/11/99 Petr Hamernik * $ */